package com.feonix.common.basedao;

import java.sql.Connection;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.GeneratedKeyHolder;

import com.feonix.common.BaseInfo;
import com.feonix.common.annotation.ColumnType;
import com.feonix.common.datasource.DataSourceManager;
import com.feonix.common.util.NumberUtil;
import com.feonix.common.util.StringUtil;
import com.feonix.common.util.jdbc.SqlLimitFactory;

public class BaseDAO {
	protected final Log logger = LogFactory.getLog(super.getClass());

	@Autowired
	protected NamedParameterJdbcTemplate jdbcHelp = null;

	@Autowired
	protected SqlLimitFactory sqlLimitFactory;
	protected String pkColumnName = null;

	/**
	 * 执行批处理
	 * 
	 * @param insertSql
	 *            sql语句
	 * @return 响应了多少行
	 */
	public int[] batchUpdate(String sql, Map<String, ?>[] paramMap) {
		return this.jdbcHelp.batchUpdate(sql, paramMap);
	}

	public boolean insert(InsertSql insertSql) {
		boolean isSuccess = false;
		String pkColumn = insertSql.getPkColumn();
		boolean bool = insertSql.isHasPk();
		if ((bool) && (StringUtil.isEmpty(pkColumn))) {
			pkColumn = this.pkColumnName;
			if (StringUtil.isEmpty(pkColumn)) {
				pkColumn = BaseInfo.getPkColumnName();
			}
		}
		GeneratedKeyHolder generatedKeyHolder = null;
		String str2 = insertSql.getSql();
		Map<String, Object> paramesMap = insertSql.getParams();
		int result = 0;
		if (bool) {
			generatedKeyHolder = new GeneratedKeyHolder();
			result = this.jdbcHelp.update(str2, new MapSqlParameterSource(paramesMap), generatedKeyHolder,
					new String[] { pkColumn });
		} else {
			result = this.jdbcHelp.update(str2, new MapSqlParameterSource(paramesMap));
		}
		if (result > 0)
			isSuccess = true;
		try {
			if ((generatedKeyHolder != null) && (generatedKeyHolder.getKey() != null)) {
				insertSql.setPkValue(Long.valueOf(generatedKeyHolder.getKey().longValue()));
			}

		} catch (InvalidDataAccessApiUsageException invalidDataAccessApiUsageException) {
			logger.error("BaseDAO insert error : " + invalidDataAccessApiUsageException.getMessage());
		} catch (DataRetrievalFailureException dataRetrievalFailureException) {
			logger.error("BaseDAO insert error : " + dataRetrievalFailureException.getMessage());
		}
		return isSuccess;
	}

	/**
	 * 修改操作
	 * 
	 * @param updateSql
	 * @return
	 */
	public boolean update(UpdateSql updateSql) {
		String str = null;
		boolean isSuccess = false;
		try {
			str = updateSql.getSql();
			Map<String, Object> params = updateSql.getParams();
			this.jdbcHelp.update(str, params);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("get updateSql error", exception);
		}
		return isSuccess;
	}

	/**
	 * 执行ddl语句，例如建表、建触发器、建序列
	 * 
	 * @param sql
	 */
	public void executeForDDL(String sql) {
		this.jdbcHelp.getJdbcOperations().execute(sql);
	}

	/**
	 * 批处理
	 * 
	 * @param sqls
	 */
	public void executeBatch(String[] sqls) {
		this.jdbcHelp.getJdbcOperations().batchUpdate(sqls);
	}

	/**
	 * 批处理
	 * 
	 * @param sql
	 * @param params
	 */
	public void executeBatch(String sql, Map<String, ?>[] params) {
		this.jdbcHelp.batchUpdate(sql, params);
	}

	public String[][] queryForArrays(Query query) {
		String[][] resultArrays = null;
		try {
			ArrayRowMap arrayRowMap = new ArrayRowMap();
			List<String[]> list = this.jdbcHelp.query(query.getSql(), query.getParams(), arrayRowMap);
			if (CollectionUtils.isNotEmpty(list)) {
				resultArrays = new String[list.size()][];
				resultArrays = list.toArray(resultArrays);
			}
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForArrays error ", emptyResultDataAccessException);
		}
		return resultArrays;
	}

	@Deprecated
	public String[][] queryForArrays(Query query, int start, int end) {
		query.setStart(Integer.valueOf(start));
		query.setEnd(Integer.valueOf(end));
		String[][] resultArrays = null;
		try {
			ArrayRowMap arrayRowMap = new ArrayRowMap();
			List<String[]> list = this.jdbcHelp.query(query.getSql(), query.getParams(), arrayRowMap);
			if (CollectionUtils.isNotEmpty(list)) {
				resultArrays = new String[list.size()][];
				resultArrays = (String[][]) list.toArray(resultArrays);
			}
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForArrays error ", emptyResultDataAccessException);
		}
		return resultArrays;
	}

	public String[] queryForArray(Query query) {
		String[] resultArray = null;
		try {
			ArrayRowMap arrayRowMap = new ArrayRowMap();
			resultArray = this.jdbcHelp.queryForObject(query.getSql(), query.getParams(), arrayRowMap);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForArray error ", emptyResultDataAccessException);
		}
		return resultArray;
	}

	public Query createQuery(String sql) {
		return new Query(sql, this.sqlLimitFactory);
	}

	public List<Map<String, Object>> queryForList(Query query) {
		return this.jdbcHelp.queryForList(query.getSql(), query.getParams());
	}

	public Map<String, Object> queryForMap(Query query) {
		Map<String, Object> resultMap = null;
		try {
			resultMap = this.jdbcHelp.queryForMap(query.getSql(), query.getParams());
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForMap error ", emptyResultDataAccessException);
		}
		return resultMap;
	}

	public <P> P queryForObject(Query query, Class<P> clazz) {
		P p = null;
		try {
			p = this.jdbcHelp.queryForObject(query.getSql(), query.getParams(), clazz);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForObject error ", emptyResultDataAccessException);
		}
		return p;
	}

	public <P> List<P> queryForObjects(Query query, Class<P> clazz) {
		List<P> resultList = null;
		try {
			resultList = this.jdbcHelp.queryForList(query.getSql(), query.getParams(), clazz);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForObjects error ", emptyResultDataAccessException);
		}
		return resultList;
	}

	public Integer queryForInteger(Query query) {
		Integer integer = null;
		try {
			integer = this.jdbcHelp.queryForObject(query.getSql(), query.getParams(), Integer.class);
			integer = Integer.valueOf(NumberUtil.getInt(integer, 0));
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			//logger.error("BaseDAO queryForInteger error ", emptyResultDataAccessException);
		}
		return integer;
	}

	public <P> P queryForBean(Query query, RowMapper<P> rowMapper) {
		P p = null;
		try {
			p = this.jdbcHelp.queryForObject(query.getSql(), query.getParams(), rowMapper);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForBean error ", emptyResultDataAccessException);
		}
		return p;
	}

	public <P> P queryForBean(Query query, Class<P> clazz) {
		P p = null;
		try {
			BeanPropertyRowMapper<P> beanPropertyRowMapper = new BeanPropertyRowMapper<P>(clazz);
			p = this.jdbcHelp.queryForObject(query.getSql(), query.getParams(), beanPropertyRowMapper);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForBean error ", emptyResultDataAccessException);
		}
		return p;
	}

	public <P> List<P> queryForBeans(Query query, RowMapper<P> rowMapper) {
		List<P> list = null;
		try {
			list = this.jdbcHelp.query(query.getSql(), query.getParams(), rowMapper);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForBeans error ", emptyResultDataAccessException);
		}
		return list;
	}

	public <P> List<P> queryForBeans(Query query, Class<P> paramClass) {
		List<P> list = null;
		try {
			BeanPropertyRowMapper<P> beanPropertyRowMapper = new BeanPropertyRowMapper<P>(paramClass);
			list = this.jdbcHelp.query(query.getSql(), query.getParams(), beanPropertyRowMapper);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForBeans error ", emptyResultDataAccessException);
		}
		return list;
	}

	public Long queryForLong(Query query) {
		Long longId = null;
		try {
			longId = this.jdbcHelp.queryForObject(query.getSql(), query.getParams(), Long.class);
			longId = Long.valueOf(NumberUtil.getLong(longId, 0L));
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
			logger.error("BaseDAO queryForLong error ", emptyResultDataAccessException);
		}
		return longId;
	}

	public boolean delete(Query query) {
		boolean isSuccess = false;
		try {
			this.jdbcHelp.update(query.getSql(), query.getParams());
			isSuccess = true;
		} catch (DataAccessException localDataAccessException) {
			this.logger.error("delete error", localDataAccessException);
		}
		return isSuccess;
	}

	public int execute(Query query) {
		return this.jdbcHelp.update(query.getSql(), query.getParams());
	}

	public boolean existsTable(String tableName) {
		boolean isSuccess = false;
		String str = "SELECT 1 FROM " + tableName + " WHERE 1=2 ";
		Query query = createQuery(str);
		try {
			queryForInteger(query);
			isSuccess = true;
		} catch (Exception exception) {
			logger.error("BaseDAO existsTable error ", exception);
		}
		return isSuccess;
	}

	public boolean createTable(String sql) {
		boolean isSuccess = false;
		try {
			executeForDDL(sql);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("createTable error", exception);
		}
		return isSuccess;
	}

	public boolean createSequence(String seqName) {
		String str = SqlBuilder.createSeq(seqName);
		boolean isSuccess = false;
		try {
			executeForDDL(str);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("createSequence error", exception);
		}
		return isSuccess;
	}

	public boolean createTrigger(String tableName, String seqName, String triggerName) {
		String str = this.pkColumnName;
		if (StringUtil.isEmpty(str)) {
			str = BaseInfo.getPkColumnName();
		}
		return createTrigger(tableName, seqName, triggerName, str);
	}

	public boolean createTrigger(String tableName, String seqName, String triggerName, String pk) {
		String str = SqlBuilder.createTrigger(triggerName, tableName, seqName, pk);
		boolean isSuccess = false;
		try {
			executeForDDL(str);
			str = SqlBuilder.enableTrigger(triggerName);
			enableTrigger(triggerName);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("createTrigger error", exception);
		}
		return isSuccess;
	}

	public boolean enableTrigger(String triggerName) {
		boolean isSuccess = false;
		String str = SqlBuilder.enableTrigger(triggerName);
		try {
			executeForDDL(str);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("enableTrigger error", exception);
		}
		return isSuccess;
	}

	public boolean disableTrigger(String triggerName) {
		boolean isSuccess = false;
		String str = SqlBuilder.disableTrigger(triggerName);
		try {
			executeForDDL(str);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("disableTrigger error", exception);
		}
		return isSuccess;
	}

	public boolean dropTrigger(String triggerName) {
		boolean isSuccess = false;
		String str = SqlBuilder.removeTrigger(triggerName);
		try {
			executeForDDL(str);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("dropTrigger error", exception);
		}
		return isSuccess;
	}

	public boolean dropTable(String tableName) {
		boolean isSuccess = false;
		String str = SqlBuilder.removeTable(tableName);
		try {
			executeForDDL(str);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("dropTable error", exception);
		}
		return isSuccess;
	}

	public boolean dropSequence(String seqName) {
		boolean isSuccess = false;
		String str = SqlBuilder.removeSeq(seqName);
		try {
			executeForDDL(str);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("dropSequence error", exception);
		}
		return isSuccess;
	}

	public boolean addColumn(String table, String column, ColumnType columnType) {
		return addColumn(table, column, columnType, null, null);
	}

	public boolean addColumn(String table, String column, ColumnType columnType, String length) {
		return addColumn(table, column, columnType, length, null);
	}

	/**
	 * 新增字段
	 * 
	 * @param table
	 * @param column
	 * @param columnType
	 * @param length
	 * @param defaultVal
	 * @return
	 */
	public boolean addColumn(String table, String column, ColumnType columnType, String length, String defaultVal) {
		boolean isSuccess = false;
		String str = this.sqlLimitFactory.getDialect().addColumn(table, column, columnType, length, defaultVal);
		try {
			executeForDDL(str);
			isSuccess = true;
		} catch (Exception exception) {
			this.logger.error("addColumn error", exception);
		}
		return isSuccess;
	}

	public String setPkColumnName() {
		return null;
	}

	public JdbcTemplate getJdbcTemplate() {
		return (JdbcTemplate) this.jdbcHelp.getJdbcOperations();
	}

	public static Connection getConnection() {
		Connection connection = null;
		try {
			connection = DataSourceUtils.getConnection(DataSourceManager.getAdaptiveDataSource());
		} catch (Exception exception) {
			exception.printStackTrace();
		}
		return connection;
	}

	public static void releaseConnection(Connection paramConnection) {
		DataSourceUtils.releaseConnection(paramConnection, DataSourceManager.getAdaptiveDataSource());
	}
}
